
    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>12、ThreadLocalMap - 学习卡片</title>
      <style>
        body { font-family: sans-serif; background-color: #f0f8ff; color: #333; display: flex; flex-direction: column; align-items: center; padding: 50px 20px; }
        .header h1 { font-size: 32px; }
        .grid-container { display: grid; grid-template-columns: repeat(3, 1fr); gap: 28px; width: 100%; max-width: 1200px; }
        .card-container { perspective: 1200px; cursor: pointer; height: 250px; }
        .card { width: 100%; height: 100%; position: relative; transform-style: preserve-3d; transition: transform 0.7s; border-radius: 16px; box-shadow: 0 4px 16px rgba(0,0,0,0.08); }
        .card-container.flipped .card { transform: rotateY(180deg); }
        .card-face { position: absolute; width: 100%; height: 100%; backface-visibility: hidden; display: flex; flex-direction: column; box-sizing: border-box; border-radius: 16px; background-color: #fff; padding: 24px; }
        .card-back { background-color: #f0fff4; transform: rotateY(180deg); justify-content: space-between; }
        .card-category { font-size: 14px; color: #0052d9; margin-bottom: 8px; font-weight: 500; }
        .card-question { font-size: 20px; font-weight: 500; flex-grow: 1; display: flex; align-items: center; justify-content: center; text-align: center; }
        .card-answer-wrapper { flex-grow: 1; overflow-y: auto; }
        .card-answer { font-size: 15px; line-height: 1.7; }
        .card-footer { font-size: 13px; color: #8a919f; border-top: 1px solid #f0f0f0; padding-top: 16px; margin-top: 16px; }
        .card-source { font-size: 13px; color: #8a919f; border-top: 1px solid #f0f0f0; padding-top: 12px; margin-top: 12px; }
      </style>
    </head>
    <body>
      <div class="header">
        <h1>12、ThreadLocalMap - 学习卡片</h1>
      </div>
      <div class="grid-container">
        
    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">机制</div>
          <div class="card-question">ThreadLocal是如何实现线程数据隔离的？其核心数据结构ThreadLocalMap具体存储在哪里？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">机制</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">ThreadLocal通过将数据存储在线程内部来实现隔离。每个Thread对象自身都拥有一个ThreadLocalMap类型的成员变量（threadLocals），这个Map的实例专门用于存储所有与该线程关联的ThreadLocal变量及其值。因此，数据实际上是存储在当前线程对象中的，而非ThreadLocal对象中。</div>
          </div>
          <div class="card-source">来源: 4.1 ThreadLocal.set方法实现</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">设计</div>
          <div class="card-question">在ThreadLocalMap中，为什么其内部类Entry的键（key）被设计为对ThreadLocal对象的弱引用（WeakReference）？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">设计</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">这是为了防止内存泄漏。如果使用强引用，当一个ThreadLocal实例在外部不再被引用时（例如方法栈中的局部变量被销毁），ThreadLocalMap依然会持有对它的强引用，导致该ThreadLocal对象无法被垃圾回收。通过使用弱引用，一旦外部没有强引用指向ThreadLocal对象，垃圾回收器就可以回收它，即使ThreadLocalMap中还存在对它的引用。</div>
          </div>
          <div class="card-source">来源: 1.3 弱引用键的设计理念</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">理论</div>
          <div class="card-question">既然ThreadLocalMap的键（key）是弱引用，为什么还会发生内存泄漏？泄漏的根本原因是什么？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">理论</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">内存泄漏的根本原因在于，虽然作为key的ThreadLocal对象被回收后，Entry中的key会变为null，但是Entry中的值（value）是强引用的。如果线程一直存活（如线程池中的线程），并且之后没有再调用get、set或remove方法触发清理机制，那么这些key为null的Entry所持有的value对象将永远不会被回收，从而造成内存泄漏。</div>
          </div>
          <div class="card-source">来源: 5.1 为什么会发生内存泄漏</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">技术</div>
          <div class="card-question">ThreadLocalMap是如何解决哈希冲突的？它与常见的HashMap有何不同？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">技术</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">ThreadLocalMap使用开放地址法中的线性探测法来解决哈希冲突。当计算出的哈希槽位已被占用时，它会向后线性查找下一个可用的槽位。这与HashMap使用链表或红黑树在同一个槽位上存储多个元素的方式不同。</div>
          </div>
          <div class="card-source">来源: 2.3 解决哈希冲突的策略</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">机制</div>
          <div class="card-question">ThreadLocalMap内部有哪些用于清理过期Entry（key为null的条目）的机制？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">机制</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">ThreadLocalMap在执行get、set和remove等操作时，会“顺手”清理过期的Entry。例如，在查找或插入过程中遇到key为null的Entry时，会触发如expungeStaleEntry、replaceStaleEntry等清理方法，移除这些无用的Entry并可能重新哈希后续的元素，以减少内存泄漏的风险。</div>
          </div>
          <div class="card-source">来源: 3.4 过期Entry的清理机制</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">模式</div>
          <div class="card-question">在线程池环境中使用ThreadLocal时，为什么强烈推荐在任务结束时手动调用remove()方法？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">模式</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">因为线程池中的线程是会被复用的。如果不在任务结束时调用remove()，与该ThreadLocal关联的数据会残留在线程的ThreadLocalMap中。当这个线程被下一个任务复用时，新任务可能会读取到上一个任务的“脏数据”，导致逻辑错误，并且这种残留的值会一直占用内存，最终导致内存泄漏。</div>
          </div>
          <div class="card-source">来源: 4.3 ThreadLocal.remove方法实现</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">特性</div>
          <div class="card-question">Java 8引入的`ThreadLocal.withInitial()`工厂方法相比于传统的匿名内部类方式，有什么优势？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">特性</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">`ThreadLocal.withInitial()`方法接受一个Supplier函数式接口作为参数来提供初始值。其优势在于代码更简洁，可以使用Lambda表达式来定义初始值逻辑，这比通过继承ThreadLocal并重写`initialValue()`方法的匿名内部类方式更符合Java 8的函数式编程风格。</div>
          </div>
          <div class="card-source">来源: 4.4 ThreadLocal.withInitial方法</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">技术</div>
          <div class="card-question">ThreadLocalMap中用来计算哈希值的`0x61c88647`这个“神奇数字”是什么？它有什么作用？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">技术</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">这个数字`0x61c88647`是斐波那契散列法（Fibonacci hashing）中的一个乘数。通过将前一个哈希码加上这个增量来生成下一个哈希码，可以使得哈希值在哈希表中（尤其是2的幂次长度的表中）分布得非常均匀，从而有效减少哈希冲突的概率。</div>
          </div>
          <div class="card-source">来源: 2.2 哈希表设计</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">机制</div>
          <div class="card-question">ThreadLocalMap提供了哪几个不同级别的过期Entry清理机制？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">机制</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">文档中总结了三个级别的清理机制：1. **定点清理**（expungeStaleEntry），清理指定位置并重新哈希后续元素；2. **探测式清理**（cleanSomeSlots），在有限步数内扫描并清理；3. **全量清理**（expungeStaleEntries），扫描整个哈希表，清理所有过期的Entry，通常在扩容前触发。</div>
          </div>
          <div class="card-source">来源: 3.4 过期Entry的清理机制</div>
        </div>
      </div>
    </div>

      </div>
    </body>
    </html>
